#include "gen.h"
#include "errors.h"

GEN genFromLong(PyObject* obj) {
	int sign = _PyLong_Sign(obj);
	int bits = _PyLong_NumBits(obj);
	if(PyErr_Occurred()) return NULL;
	int byte_length = bits/8+1; // include the sign bit
	int long_length = (byte_length - 1) / BYTES_IN_LONG + 1;
	byte_length = long_length*BYTES_IN_LONG;
	unsigned char* byte_arr = (unsigned char*) malloc(byte_length);
	_PyLong_AsByteArray((PyLongObject*) obj, byte_arr, byte_length, true, true);
	if(PyErr_Occurred()) {
		free(byte_arr);
		return NULL;
	}
	CATCH_PARI {
		free(byte_arr);
		SET_ERROR;
		return NULL;
	} TRY_PARI {
		GEN res = cgeti(long_length+2);
		res[1] = evalsigne(_PyLong_Sign(obj)) | evallgefint(long_length+2);
		GEN ptr = int_LSW(res);
		int shift = 0;
		for(int i = 0; i<byte_length; i+=BYTES_IN_LONG) {
			*ptr=0;
			for(int j = 0; j<BYTES_IN_LONG; j++) {
				long val = 0xFFL&byte_arr[i+j];
				if(sign<0) {
					val = 0xFFL&(-shift - val);
					shift = (val>=1-shift) ? 1 : 0;
				}
				*ptr += val<<(8*j);
			}
			ptr = int_nextW(ptr);
		}
		res = int_normalize(res, 0);
		free(byte_arr);
		return res;
	} ENDCATCH_PARI
}

GEN gen_cast(PyObject* obj) {
    if(obj==NULL) return NULL;
	GEN res = NULL;
	CATCH_PARI {
		SET_ERROR;
		return NULL;
	} TRY_PARI {
		if(obj->ob_type==type_ptr) {
			res = ((PyGEN*) obj)->val;
		} else if(PyLong_Check(obj)) {
			res = genFromLong(obj);
		} else if(PyInt_Check(obj)) {
			res = stoi(PyInt_AsLong(obj));
		} else if(PyFloat_Check(obj)) {
			res = dbltor(PyFloat_AsDouble(obj));
		} else if(PyTuple_Check(obj)) {
			int size = PyTuple_Size(obj);
			res = cgetg(size+1, t_VEC);
			for(int i = 0; i<size; i++) {
				GEN el = gen_cast(PyTuple_GetItem(obj, i));
				if(el==NULL) return NULL;
				el=gclone(el);
				gel(res, i+1) = el;
			}
		} else if(PyList_Check(obj)) {
			int size = PyList_Size(obj);
			res = cgetg(size+1, t_VEC);
			for(int i = 0; i<size; i++) {
				GEN el = gen_cast(PyList_GetItem(obj, i));
				if(el==NULL) return NULL;
				el=gclone(el);
				gel(res, i+1) = el;
			}
		} else if(PyString_Check(obj)) {
			res = gp_read_str(PyString_AsString(obj));
		} else {
			wrong_parameter("GEN-compatible", obj);
		}
	} ENDCATCH_PARI
	if(PyErr_Occurred()) return NULL;
	return res;
}
